import User from '@/models/system/user.model';
import Role from '@/models/system/role.model';
import Menu from '@/models/system/menu.model';
import JwtUtil from '@/utils/security/jwt.util';
import AsyncManager from '@/utils/async/AsyncManager';
import AsyncFactory from '@/utils/async/AsyncFactory';
import passwordService from '@/services/system/password.service';
import userOnlineService from '@/services/monitor/user-online.service';
import { Op, WhereOptions } from 'sequelize';
import jwtConfig from '@/config/jwt';
import Dept from '@/models/system/dept.model';
import { setJwtUuidMapping, getUuidByJwt, deleteJwtUuidMapping } from '@/services/monitor/user-online.service';
import { v4 as uuidv4 } from 'uuid';
import { getLogger } from '@/utils/logger';

// 创建常规日志记录器（不输出到控制台）
const logger = getLogger('auth-service', {
  console: false
});
// 创建控制台日志记录器
const consoleLogger = getLogger('auth-service');

class AuthService {
  /**
   * 用户登录
   * @param username 用户名
   * @param password 密码
   * @param ip 登录IP
   * @param userAgent 用户代理字符串
   * @returns 登录结果
   */
  async login(username: string, password: string, ip: string = '127.0.0.1', userAgent: string = '') {
    try {
      // 使用consoleLogger记录操作开始
      consoleLogger.info(`用户登录: username=${username}, ip=${ip}`);
      
      // 检查账户是否被锁定
      logger.debug(`检查账户是否被锁定: username=${username}`);
      const isLocked = await passwordService.isAccountLocked(username);
      if (isLocked) {
        // 记录登录日志
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, 'Fail', '密码输入错误次数过多，账户被锁定'));
        logger.info(`登录失败: username=${username}, 原因=账户被锁定`);
        return {
          code: 500,
          msg: '密码输入错误次数过多，账户被锁定'
        };
      }

      // 查询用户
      logger.debug(`查询用户信息: username=${username}`);
      const user = await User.findOne({
        where: {
          userName: username,
          delFlag: '0'
        }
      });

      // 用户不存在
      if (!user) {
        // 记录登录日志
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, 'Fail', '登录用户不存在'));
        // 增加失败计数
        await passwordService.loginFail(username);
        logger.info(`登录失败: username=${username}, 原因=用户不存在`);
        return {
          code: 500,
          msg: '用户名或密码错误'
        };
      }

      // 用户被禁用
      if (user.status === '1') {
        // 记录登录日志
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, 'Fail', '用户已被停用'));
        logger.info(`登录失败: username=${username}, 原因=用户已被禁用`);
        return {
          code: 500,
          msg: '用户已被禁用，请联系管理员'
        };
      }

      // 密码错误
      if (!user.validatePassword(password)) {
        // 记录登录日志
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, 'Fail', '密码验证失败'));
        // 增加失败计数
        logger.debug(`密码验证失败，增加失败计数: username=${username}`);
        const isLocked = await passwordService.loginFail(username);
        logger.info(`登录失败: username=${username}, 原因=密码错误, 账户是否锁定=${isLocked}`);
        return {
          code: 500,
          msg: isLocked ? '密码输入错误次数过多，账户已被锁定' : '用户名或密码错误'
        };
      }

      // 登录成功，清除失败计数
      logger.debug(`登录成功，清除失败计数: username=${username}`);
      await passwordService.loginSuccess(username);

      // 查询用户角色
      logger.debug(`查询用户角色: userId=${user.userId}`);
      const roles = await Role.findAll({
        include: [
          {
            model: User,
            where: { userId: user.userId },
            attributes: [],
            as: 'users'
          }
        ],
        where: {
          status: '0',
          delFlag: '0'
        }
      });

      // 获取角色标识列表
      const roleList = roles.map((role) => role.get('roleKey'));
      logger.debug(`用户角色列表: ${roleList.join(', ')}`);

      // 获取菜单权限标识列表
      logger.debug(`获取菜单权限标识列表: userId=${user.userId}`);
      const permissions = await this.getPermissions(user.userId);

      // 构建令牌用户信息
      let deptName = '';
      if (user.deptId) {
        logger.debug(`查询用户部门: deptId=${user.deptId}`);
        const dept = await Dept.findByPk(user.deptId);
        deptName = dept ? dept.deptName : '';
      }
      const userInfo = {
        userId: user.userId,
        userName: user.userName,
        roles: roleList,
        permissions: roleList.includes('admin') ? ['*'] : permissions,
        deptName
      };

      // 生成访问令牌和刷新令牌
      logger.debug(`生成访问令牌和刷新令牌: userId=${user.userId}`);
      const token = JwtUtil.generateToken(userInfo);
      const refreshToken = JwtUtil.generateRefreshToken(userInfo);

      // 计算令牌过期时间（秒）
      const expireTime = this.getTokenExpireSeconds(jwtConfig.expiresIn);
      
      // 生成uuid作为缓存key
      const uuid = uuidv4();
      logger.debug(`生成UUID作为缓存键: ${uuid}`);
      
      // 更新用户登录信息
      logger.debug(`更新用户登录信息: userId=${user.userId}, ip=${ip}`);
      user.loginIp = ip;
      user.loginDate = new Date();
      await user.save();
      
      // 创建在线用户记录
      logger.debug(`创建在线用户记录: userId=${user.userId}, uuid=${uuid}`);
      const userOnline = await userOnlineService.loginUserToUserOnline(uuid, userInfo, ip, userAgent);
      await userOnlineService.saveOnlineUser(uuid, userOnline, expireTime);
      
      // 建立jwt-uuid映射
      logger.debug(`建立JWT-UUID映射: uuid=${uuid}`);
      await setJwtUuidMapping(token, uuid, expireTime);

      // 记录登录成功日志
      AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, 'Success', '登录成功'));
      
      // 记录操作结果
      logger.info(`登录成功: username=${username}, userId=${user.userId}`);

      return {
        code: 200,
        msg: '登录成功',
        data: {
          token,
          refreshToken
        }
      };
    } catch (error) {
      // 记录登录异常日志
      consoleLogger.error(`登录失败: ${error instanceof Error ? error.message : String(error)}`);
      AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, 'Fail', `登录异常：${error instanceof Error ? error.message : String(error)}`));
      throw error;
    }
  }

  /**
   * 刷新令牌
   * @param refreshToken 刷新令牌
   * @returns 新的访问令牌
   */
  async refreshToken(refreshToken: string) {
    try {
      // 使用consoleLogger记录操作开始
      consoleLogger.info(`刷新令牌`);
      
      // 验证刷新令牌
      logger.debug(`验证刷新令牌`);
      const userInfo = JwtUtil.verifyToken(refreshToken);

      if (!userInfo) {
        logger.info(`刷新令牌失败: 令牌已过期或无效`);
        return {
          code: 401,
          msg: '刷新令牌已过期或无效'
        };
      }

      // 查询用户是否仍然有效
      logger.debug(`查询用户是否仍然有效: userId=${userInfo.userId}`);
      const user = await User.findOne({
        where: {
          userId: userInfo.userId,
          delFlag: '0',
          status: '0'
        }
      });

      if (!user) {
        logger.info(`刷新令牌失败: 用户不存在或已被禁用, userId=${userInfo.userId}`);
        return {
          code: 401,
          msg: '用户不存在或已被禁用'
        };
      }

      // 重新生成访问令牌
      logger.debug(`重新生成访问令牌: userId=${userInfo.userId}`);
      const newToken = JwtUtil.generateToken(userInfo);

      // 查找uuid
      const oldJwt = refreshToken;
      logger.debug(`查找JWT对应的UUID`);
      const uuid = await getUuidByJwt(oldJwt);
      if (uuid) {
        // 计算新过期时间
        const expireTime = this.getTokenExpireSeconds(jwtConfig.expiresIn);
        
        // 更新jwt-uuid映射
        logger.debug(`更新JWT-UUID映射: uuid=${uuid}`);
        await setJwtUuidMapping(newToken, uuid, expireTime);
        await deleteJwtUuidMapping(oldJwt);
        
        // 刷新login_tokens:{uuid}过期时间
        logger.debug(`刷新用户在线记录过期时间: uuid=${uuid}`);
        const cacheResult = await userOnlineService.getOnlineUserByUuid?.(uuid);
        if (cacheResult && cacheResult.loginTime) {
          // 重新设置缓存
          await userOnlineService.saveOnlineUser(uuid, cacheResult, expireTime);
        }
      }
      
      // 记录操作结果
      logger.info(`刷新令牌成功: userId=${userInfo.userId}`);

      return {
        code: 200,
        msg: '刷新令牌成功',
        data: {
          token: newToken
        }
      };
    } catch (error) {
      consoleLogger.error(`刷新令牌失败: ${error instanceof Error ? error.message : String(error)}`);
      throw new Error('刷新令牌失败');
    }
  }

  /**
   * 登出
   * @param userId 用户ID
   * @param username 用户名
   * @param token 令牌
   * @returns 登出结果
   */
  async logout(userId: number, username: string, token?: string) {
    try {
      // 使用consoleLogger记录操作开始
      consoleLogger.info(`用户登出: userId=${userId}, username=${username}`);
      
      // 记录登出日志
      logger.debug(`记录登出日志: username=${username}`);
      AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, 'Success', '退出成功'));
      
      // 如果提供了令牌，从Redis中删除会话信息
      if (token) {
        // 查找uuid
        logger.debug(`查找JWT对应的UUID: userId=${userId}`);
        const uuid = await getUuidByJwt(token);
        if (uuid) {
          logger.debug(`强制退出用户: uuid=${uuid}`);
          await userOnlineService.forceLogout(uuid);
          await deleteJwtUuidMapping(token);
        }
      }
      
      // 记录操作结果
      logger.info(`登出成功: userId=${userId}, username=${username}`);
      
      return {
        code: 200,
        msg: '退出成功'
      };
    } catch (error) {
      consoleLogger.error(`登出失败: ${error instanceof Error ? error.message : String(error)}`);
      throw new Error('登出失败');
    }
  }

  /**
   * 获取用户权限标识列表
   * @param userId 用户ID
   * @returns 权限标识列表
   */
  async getPermissions(userId: number): Promise<string[]> {
    try {
      // 使用consoleLogger记录操作开始
      consoleLogger.info(`获取用户权限标识列表: userId=${userId}`);
      
      // 超级管理员拥有所有权限
      logger.debug(`检查用户是否为管理员: userId=${userId}`);
      const isAdmin = await this.isAdmin(userId);
      if (isAdmin) {
        logger.info(`用户为管理员，拥有所有权限: userId=${userId}`);
        return ['*'];
      }

      // 查询用户权限
      logger.debug(`查询用户菜单权限: userId=${userId}`);
      const menus = await Menu.findAll({
        include: [
          {
            model: Role,
            as: 'Roles',
            include: [
              {
                model: User,
                as: 'users',
                where: { userId },
                attributes: []
              }
            ],
            attributes: [],
            where: {
              status: '0',
              delFlag: '0'
            }
          }
        ],
        where: {
          status: '0',
          [Op.and]: [
            {
              [Op.or]: [
                { perms: { [Op.ne]: '' } },
                { perms: { [Op.ne]: null } }
              ]
            }
          ]
        } as WhereOptions
      });

      // 获取权限字符串
      const perms = menus.map((menu) => menu.get('perms')).filter((perm): perm is string => Boolean(perm));

      // 将多个权限合并并去重
      const permList = [...new Set(perms.flatMap(perm => perm.split(',')))];
      
      // 记录操作结果
      logger.info(`获取用户权限标识列表完成: userId=${userId}, 权限数量=${permList.length}`);

      return permList;
    } catch (error) {
      consoleLogger.error(`获取用户权限标识列表失败: ${error instanceof Error ? error.message : String(error)}`);
      throw new Error('获取用户权限标识列表失败');
    }
  }

  /**
   * 判断用户是否为管理员
   * @param userId 用户ID
   * @returns 是否为管理员
   */
  async isAdmin(userId: number): Promise<boolean> {
    try {
      // 使用consoleLogger记录操作开始
      consoleLogger.info(`判断用户是否为管理员: userId=${userId}`);
      
      logger.debug(`查询用户信息: userId=${userId}`);
      const user = await User.findByPk(userId);
      if (!user) {
        logger.info(`用户不存在: userId=${userId}`);
        return false;
      }

      // 判断是否为admin用户
      if (user.userName === 'admin') {
        logger.info(`用户为admin账户: userId=${userId}`);
        return true;
      }

      // 判断是否有admin角色
      logger.debug(`查询用户是否有admin角色: userId=${userId}`);
      const adminRole = await Role.findOne({
        include: [
          {
            model: User,
            as: 'users',
            where: { userId },
            attributes: []
          }
        ],
        where: {
          roleKey: 'admin',
          status: '0',
          delFlag: '0'
        }
      });
      
      const isAdminRole = !!adminRole;
      // 记录操作结果
      logger.info(`判断用户是否为管理员完成: userId=${userId}, 是否为管理员=${isAdminRole}`);

      return isAdminRole;
    } catch (error) {
      consoleLogger.error(`判断用户是否为管理员失败: ${error instanceof Error ? error.message : String(error)}`);
      throw new Error('判断用户是否为管理员失败');
    }
  }

  /**
   * 获取令牌过期时间（秒）
   * @param expiresIn 过期时间字符串，如 '24h', '30m', '2d' 等
   * @returns 过期时间（秒）
   */
  private getTokenExpireSeconds(expiresIn: string): number {
    try {
      logger.debug(`计算令牌过期时间: expiresIn=${expiresIn}`);
      
      const match = expiresIn.match(/^(\d+)([smhd])$/);
      if (!match) {
        // 默认24小时
        logger.debug(`无效的过期时间格式，使用默认值: 24小时`);
        return 24 * 60 * 60;
      }

      const [, value, unit] = match;
      const numValue = parseInt(value, 10);

      // 根据单位计算秒数
      let seconds: number;
      switch (unit) {
        case 's':
          seconds = numValue;
          break;
        case 'm':
          seconds = numValue * 60;
          break;
        case 'h':
          seconds = numValue * 60 * 60;
          break;
        case 'd':
          seconds = numValue * 24 * 60 * 60;
          break;
        default:
          logger.debug(`无效的时间单位: ${unit}，使用默认值: 24小时`);
          seconds = 24 * 60 * 60; // 默认24小时
      }
      
      logger.debug(`令牌过期时间计算完成: ${expiresIn} = ${seconds}秒`);
      return seconds;
    } catch (error) {
      consoleLogger.error(`计算令牌过期时间失败: ${error instanceof Error ? error.message : String(error)}`);
      // 私有方法，返回默认值而不抛出异常
      return 24 * 60 * 60; // 默认24小时
    }
  }

  /**
   * 获取用户详细信息，包括部门和角色
   * @param userId 用户ID
   * @returns 用户详细信息
   */
  async getUserDetailInfo(userId: number) {
    try {
      // 使用consoleLogger记录操作开始
      consoleLogger.info(`获取用户详细信息: userId=${userId}`);
      
      // 查询用户信息，包括部门和角色
      logger.debug(`查询用户信息: userId=${userId}`);
      const user = await User.findOne({
        where: {
          userId,
          delFlag: '0'
        },
        include: [
          {
            model: Role,
            through: { attributes: [] }, // 不包含中间表字段
            where: {
              status: '0', // 只获取正常状态的角色
              delFlag: '0'
            },
            required: false, // 允许没有角色的用户
            as: 'roles'  // 添加正确的别名
          },
          {
            model: Dept,
            as: 'dept', // 设置关联别名
            required: false // 允许没有部门的用户
          }
        ],
        attributes: { exclude: ['password'] } // 排除密码字段
      });

      if (!user) {
        logger.info(`用户不存在: userId=${userId}`);
        return null;
      }

      // 转换成JSON对象，使用any类型避开类型检查
      const userJson: any = user.toJSON();
      
      // 处理角色信息
      const roles = userJson.roles || [];
      logger.debug(`处理用户角色信息: userId=${userId}, 角色数量=${roles.length}`);
      
      // 为角色添加admin标识
      roles.forEach((role: any) => {
        role.admin = role.roleKey === 'admin';
      });
      
      // 删除不需要的关联名
      delete userJson.roles;
      
      // 添加处理后的roles数组
      userJson.roles = roles;
      
      // 记录操作结果
      logger.info(`获取用户详细信息完成: userId=${userId}, 角色数量=${roles.length}`);
      
      return userJson;
    } catch (error) {
      consoleLogger.error(`获取用户详细信息失败: ${error instanceof Error ? error.message : String(error)}`);
      throw new Error('获取用户详细信息失败');
    }
  }
}

export default new AuthService(); 